package 多线程.力扣1114_按序打印;

public class OrderedExecution2 {

    // 状态变量，1=T1执行, 2=T2执行, 3=T3执行
    private int turn = 1;

    public void printOne() {
        System.out.println(Thread.currentThread().getName() + ": one");
        // 更新状态，轮到下一个线程
        turn = 2;
    }

    public void printTwo() {
        while (turn != 2);
        System.out.println(Thread.currentThread().getName() + ": two");
        turn = 3;
    }

    public void printThree() {
        while (turn != 3);
        System.out.println(Thread.currentThread().getName() + ": three");
    }

    public static void main(String[] args) {
        OrderedExecution2 orderedExecution = new OrderedExecution2();

        Thread t1 = new Thread(orderedExecution::printOne, "T1");
        Thread t2 = new Thread(orderedExecution::printTwo, "T2");
        Thread t3 = new Thread(orderedExecution::printThree, "T3");

        // 这种由于缓存和内容更新不及时，即使turn更改了，其他线程也未必能看到，就导致可能卡死
        // 但是加上volatile关键字后，其他线程能及时看到turn的变化，就可以运行了。虽有盲等，但效率极高
        t2.start();
        t3.start();
        t1.start();


    }
}
/*
 * volatile关键字的作用是确保变量的可见性和禁止指令重排序。
 * 0. 禁止指令重排序的作用，以及使用范围
 * 在多线程环境中，指令重排序可能导致线程看到的变量状态不一致。
 * 不单单volatile有这个功能，其他的锁机制（如synchronized、Lock等）也能禁止指令重排序。
 *
 * 1.如何禁止指令重排序？
 * 内存屏障
 *  禁止屏障两侧的指令重排序：栅栏前方的所有读写操作都必须执行完毕后，才能执行栅栏后方的读写操作。
    强制刷出/读入内存：确保屏障指令前的写操作结果都对其他处理器可见（强制写回主内存），
    *               并让屏障指令后的读操作能看到其他处理器的写操作结果（强制从主内存读）。
    * volatile是如何使用内存屏障的？
    JVM会针对volatile变量的读写操作，在生成的字节码中插入相应的内存屏障指令：
    在每个volatile写操作之前，插入一个StoreStore屏障，保证之前的普通写操作不会被重排到volatile写之后。
    在每个volatile写操作之后，插入一个StoreLoad屏障，保证volatile的写操作对其他线程可见，并防止它与后面的读操作重排。
    在每个volatile读操作之后，插入一个LoadLoad屏障 和 LoadStore屏障，防止volatile读与后面的普通读/写重排。
 *
 */